# encoding: utf-8

require "cgi"
require "net/http"
require "uri"
require "nokogiri"
require "open-uri"
require "provider.rb"

module Ship
	class Correios < Ship::Provider
		PAC_SEM_CONTRATO            = 0
		SEDEX_SEM_CONTRATO          = 1
		SEDEX_A_COBRAR_SEM_CONTRATO = 2
		SEDEX_10_SEM_CONTRATO       = 3
		SEDEX_HOJE_SEM_CONTRATO     = 4
		SEDEX_COM_CONTRATO          = 5
		ESEDEX_COM_CONTRATO         = 8
		PAC_COM_CONTRATO            = 9

		CAIXA_PACOTE                = 0
		ROLO_PRISMA                 = 1

		def initialize
			super("Correios",self.class.countries,self.class.services,self.class.formats)
		end

		def cost(shipping)
			params = {}
			params["nCdEmpresa"]          = shipping.user     || ""
			params["sDsSenha"]            = shipping.password || ""
			params["sCepOrigem"]          = shipping.from     || ""
			params["sCepDestino"]         = shipping.to       || ""
			params["nVlPeso"]             = shipping.weight   || 0
			params["nVlComprimento"]      = shipping.length   || 0
			params["nVlAltura"]           = shipping.height   || 0
			params["nVlLargura"]          = shipping.width    || 0
			params["nVlValorDeclarado"]   = shipping.value    ? shipping.value.round : 0
			params["sCdMaoPropria"]       = shipping.in_hands ? "s" : "n"
			params["sCdAvisoRecebimento"] = shipping.return_receipt ? "s" : "n" 
			params["nCdFormato"]          = shipping.format   ? shipping.format.code : 0
			params["nVlDiametro"]         = shipping.diameter || 0
			params["StrRetorno"]          = "xml"

			if shipping.service.kind_of?(Array)
				params["nCdServico"]	= shipping.service.map {|s| s.code}.join(",")
			else				
				params["nCdServico"]	= shipping.service ? shipping.service.code : ""
			end				

      params_str = params.map {|key,val| "#{CGI.escape(key.to_s)}=#{CGI.escape(val.to_s)}"}.join("&")
      url        = URI.parse("http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx/CalcPrecoPrazo?#{params_str}")
      data       = open(url.to_s)
			raise Exception.new("Could not get an answer from URL: #{full_path}") if data.status[0]!="200"

      data = data.read
			error_msg = data.scan(/(<Erro>)([0-9-]{1,5})(<\/Erro>)(<MsgErro>)([a-z0-9 ]+)(<\/MsgErro>)(.*)/i)
			raise Exception.new(error_msg[0][4]) if error_msg.size>0 && error_msg[0][1]!="0"

			xml = Nokogiri::XML(data)
			rtn = {}

			xml.search("Servicos/cServico").each do |cServico|
        values = {
                "Codigo"=>0,
                "Valor"=>0, 
                "PrazoEntrega"=>0,
                "ValorMaoPropria"=>0,
                "ValorAvisoRecebimento"=>0,
                "ValorValorDeclarado"=>0,
                "EntregaDomiciliar"=>0,
                "EntregaSabado"=>0
              }
        values.each {|k,v| values[k] = cServico.search("#{k}").first.text}
        rtn[values["Codigo"]] = values
			end
			rtn
    rescue => e
      STDERR.puts "error: #{e}"
      []
		end

		def where(shipping)
			raise Exception.new("No shipping info") if shipping.nil? || shipping.id.nil?
			parms = {
				"p_itemcode"	=> "",
				"p_lingua"		=> "001",
				"p_teste="		=> "",
				"p_tipo"			=> "003",
				"z_action"		=> "",
				"p_cod_lis"		=> shipping.id
			}
			url   = "http://websro.correios.com.br/sro_bin/txect01$.inexistente?"+parms.map {|k,v| "#{k}=#{v}"}.join("&")	
			data  = open(URI.parse(url).to_s).read
			raise Exception.new("Could not get an answer from URL") if data.size<1
      doc   = Nokogiri::HTML(data)
      rst   = []
      ldate = nil
      doc.search("tr").each do |tr|
        tds     = tr.search("td")
        matches = /^(?<date>\d{2}\/\d{2}\/\d{4} \d{2}:\d{2})|(?<address>Endereço)/.match(tds[0])
        next if !matches
        date   = matches[:date] || ldate 
        ldate  = date 
        desc   = matches[:date] ? tds[1].text : tds[0].text
        status = matches[:date] ? tds[2].text : "Postado"
        rst << {date: date.strip, desc: desc.strip, status: status.strip}
      end
      rst.reverse
    rescue => e
      STDOUT.puts "error: #{e}"
      []
		end

		def self.countries
			[Country.new("Brasil","BRA")]
		end

		def self.services
			[
				Service.new("PAC sem contrato"            ,41106,false),
				Service.new("SEDEX sem contrato"          ,40010,false),
				Service.new("SEDEX a cobrar, sem contrato",40045,false),
				Service.new("SEDEX 10, sem contrato"      ,40215,false),
				Service.new("SEDEX Hoje, sem contrato"    ,40290,false),
				Service.new("SEDEX com contrato"          ,40096,true ),
				Service.new("SEDEX com contrato"          ,40436,true ),
				Service.new("SEDEX com contrato"          ,40444,true ),
				Service.new("e-SEDEX, com contrato"       ,81019,true ),
				Service.new("PAC com contrato"            ,41068,true)
			]
		end

		def self.formats
			[
				Format.new("Caixa/pacote",1),
				Format.new("Rolo/prisma",2)
			]
		end
	end
end
